﻿using RPAVIEW.util;
using RPAVIEW.views.CommonPages;
using SUNRPA.CONTROLS.IMPL.controls;
using SUNRPA.CONTROLS.IMPL.domain;
using SUNRPA.CONTROLS.IMPL.domain.constant;
using SUNRPA.CONTROLS.IMPL.domain.flowItemParam;
using SUNRPA.CONTROLS.IMPL.domain.flowRunningVar;
using SUNRPA.CONTROLS.IMPL.interfaces;
using SUNRPA.CONTROLS.IMPL.util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using System.Windows.Controls;

namespace RPAVIEW.views.EditPage {
    public class TaskRunner {
        public static LogUtil logger = new LogUtil(typeof(TaskRunner));
        public ConcurrentDictionary<string, ConcurrentDictionary<string, RPAVariableDefineWrapper>> runnerContextVarData;
        List<string> flowItemInfos;
        //原窗口
        private Window originWindow;
        //原窗口状态
        private WindowState originState;
        //当前执行步骤索引
        public int executeingStep = 0;
        //当前执行步骤内容
        public string executeContent = "";
        private AutoResetEvent consumeEvent;
        private AutoResetEvent produceEvent;

        public TaskRunner(Window mainWindow) {
            originWindow = mainWindow;
            originState = mainWindow.WindowState;
            //创建运行器变量存储上下文容器
            runnerContextVarData = new ConcurrentDictionary<string, ConcurrentDictionary<string, RPAVariableDefineWrapper>>();
            ConcurrentDictionary<string, RPAVariableDefineWrapper> globleParam = new ConcurrentDictionary<string, RPAVariableDefineWrapper>();
            ConcurrentDictionary<string, RPAVariableDefineWrapper> runningParam = new ConcurrentDictionary<string, RPAVariableDefineWrapper>();
            runnerContextVarData.TryAdd(DefaultContents.GLOBLE_PARAM, globleParam);
            runnerContextVarData.TryAdd(DefaultContents.RUNNING_PARAM, runningParam);
            BaseControlActions.updateInfoDelegate = new BaseControlActions.UpdateInfoDelegate(UpdateInfo);
        }
        
        public void RunTask(List<FlowItem> flowItems, List<string> flowItemInfos) {
            //严令禁止，一个RPA进程同时只能有一个任务运行
            lock(Application.Current) { 
                logger.Info("流程开始执行");
                consumeEvent = new AutoResetEvent(false);
                produceEvent = new AutoResetEvent(false);
                this.flowItemInfos = flowItemInfos;
                int count = flowItems.Count;
                for(int i = 0; i < count; i++) {
                    FlowItem item = flowItems[i];
                    BaseParam? param = item.ControlParam as BaseParam;
                    //创建委托
                    if(param != null && param.hasDelegate) {
                        IDelegateInterface deleObj = (IDelegateInterface)Activator.CreateInstance(Type.GetType(param.delegateType));
                        param.delegateObj = (Control)deleObj;
                        param.actionDelegate = new BaseParam.ActionDelegate(deleObj.DelegateMethod);
                    }
                }
                //1、隐藏主窗口 
                //2、右下角显示任务进度窗口
                //3、执行流程，并改变进度信息
                //4、关闭右下角窗口（之后有了日志框架后需要同步日志），打开主窗口
                RunnerInfoWin infoWin = Change2RunnerWin();
                infoWin.Show();
                try {
                    Thread runnerTask = Run(flowItems, flowItemInfos);
                    infoWin.taskThread = runnerTask;
                    while(true) {
                        //用来更新提示框数据
                        consumeEvent.WaitOne(100);
                        //执行成功后推出循环
                        ThreadState state = runnerTask.ThreadState;
                        if(state.Equals(ThreadState.Stopped) || state.Equals(ThreadState.Aborted)) {
                            break;
                        }
                        infoWin.stepInfo.Text = "当前正在运行第" + executeingStep + "步:" + executeContent;
                        System.Windows.Forms.Application.DoEvents();
                        produceEvent.Set();
                    }
                } finally {
                    //运行结束后将运行时变量重置为空
                    ConcurrentDictionary<string, RPAVariableDefineWrapper> runningData = runnerContextVarData[DefaultContents.RUNNING_PARAM];
                    foreach(string key in runningData.Keys) { 
                        runningData[key].Variable = null;
                    }
                    consumeEvent.Dispose();
                    produceEvent.Dispose();
                    //无论什么情况窗口都要恢复原状
                    infoWin.stepInfo.Text = "运行结束";
                    System.Windows.Forms.Application.DoEvents();
                    //这里的额就固定1秒
                    Thread.Sleep(1000);
                    ChangeWinBack(infoWin);
                }
            }
        }

        private Thread Run(List<FlowItem> flowItems, List<string> flowItemInfos) { 
            Thread runnerTask = new Thread(() => {
                int count = flowItems.Count;
                for(int i = 0; i < flowItems.Count; i++) {
                    FlowItem item = flowItems[i];
                    BaseParam param = item.ControlParam;
                    int exceptionHandleType = param.ExceptionHandler;
                    try {
                        Type actionType = Type.GetType(BaseNameSpace.FLOW_CONTROL_BASE_PATH + item.ControlKey + "Action," + DefaultContents.PROJECT_NAME);
                        BaseControlActions action = (BaseControlActions)Activator.CreateInstance(actionType);
                        //暂时默认每个任务之间的执行间隔为1秒，后期可以做全局配置来控制间隔
                        //增加步骤
                        BaseControlActions.updateInfoDelegate(i);
                        logger.Info("正在运行第{step}步：{stepInfo}", executeingStep, executeContent);
                        //更新运行信息
                        consumeEvent.Set();
                        i = action.Execute(i, item, runnerContextVarData, flowItems);
                        if(i < count - 1) { 
                            produceEvent.WaitOne();
                        }
                    } catch(Exception ex) {
                        if(!ex.GetType().Equals(typeof(ThreadAbortException))) { 
                            consumeEvent.Set();
                            logger.Error("流程运行出错", ex, null);
                            //此处应该添加日志记录，添加日志框架和本地云端日志收集框架
                            if(0 == exceptionHandleType) {
                                MessageBox.Show("运行出错"+ex.Message);
                                logger.Error("第{step}步，{content}，出错：终止流程", null, i + 1, flowItemInfos[i]);
                                break;
                            } else if(1 == exceptionHandleType) {
                                logger.Error("第{step}步，{content}，出错：跳过该步骤", null, i + 1, flowItemInfos[i]);
                                //如果是流程块途中出错，想要跳过此步骤需要直接跳到流程块尾部
                                if(item.EndTag != null) {
                                    i = item.EndTag.Index;
                                }
                                continue;
                            } else {
                                logger.Error("第{step}步，{content}，出错：重试该步骤", null, i + 1, flowItemInfos[i]);
                                break;
                                //重试任务暂且搁置，之后做了重试次数的限制后再做
                            }
                        } else { 
                            MessageBox.Show("终止线程成功");
                            logger.Error("用户在第{step}步，{content}，手动终止线程", null, i + 1, flowItemInfos[i]);
                        }
                    }
                    Thread.Sleep(500);
                }
                logger.Info("流程运行结束");
                consumeEvent.Set();
            });
            //设置为后台线程
            runnerTask.IsBackground = true;
            runnerTask.Start();
            return runnerTask;
        }

        private void UpdateInfo(int stepNum) { 
            this.executeingStep = stepNum + 1;
            this.executeContent = flowItemInfos[stepNum];
            consumeEvent.Set();
            produceEvent.WaitOne(1000);
        }
         
        public RunnerInfoWin Change2RunnerWin() { 
            originWindow.WindowState = WindowState.Minimized;
            RunnerInfoWin infoWin = new RunnerInfoWin();
            infoWin.Left = SystemParameters.WorkArea.Width - infoWin.Width;
            infoWin.Top = SystemParameters.WorkArea.Height - infoWin.Height;
            //infoWin.ShowInTaskbar = false;
            infoWin.Topmost = true;
            return infoWin;
        }

        public void ChangeWinBack(RunnerInfoWin infoWin) { 
            //originWindow.ShowInTaskbar = true;
            originWindow.WindowState = originState;
            originWindow.Show();
            originWindow.Activate();
            User32Utils.SetForegroundWindow(ElementUtil.getWindowPtr(originWindow));
            infoWin.Close();
        }
    }
}
